package com.disapand.strongarmy.service.impl

import cn.dev33.satoken.secure.*
import cn.dev33.satoken.stp.*
import com.disapand.strongarmy.model.*
import com.disapand.strongarmy.service.*
import com.disapand.strongarmy.vo.*
import com.disapand.strongarmy.vo.account.*
import org.ktorm.database.*
import org.ktorm.dsl.*
import org.ktorm.entity.*
import org.ktorm.support.mysql.*
import org.springframework.stereotype.*
import java.time.*
import kotlin.math.*

/**
 *
 * @auth disapand
 * @date 2021/10/12 - 21:52 - 星期二
 *
 */
@Service
class AccountServiceImpl(
    private val database: Database
) : AccountService {

    /**
     * 添加新用户
     *
     * @param account
     * @param password
     * @param username
     * @param avatar
     * @return
     */
    override fun add(account: String, password: String, username: String, avatar: String): Boolean {
        val userId = StpUtil.getLoginIdAsInt()
        val encryptPassword = SaSecureUtil.sha1(password)

        val admin = Account {
            this.account = account
            this.password = encryptPassword
            this.username = username
            this.avatar = avatar
            this.createdBy = userId
        }

        val nums = database.accounts.add(admin)
        return nums > 0
    }

    /**
     * 根据 id 查找账号
     *
     * @param id
     * @return
     */
    override fun get(id: Int): Account? {
        return database.accounts.filterColumns {
            it.columns - it.password - it.deleted - it.createdAt
        }.find { (it.id eq id) and (it.deleted eq false) }
    }

    /**
     * 根据 account 查找账号
     *
     * @param account
     * @return
     */
    override fun get(account: String): Account? {
        return database.accounts.filterColumns {
            it.columns - it.password - it.deleted - it.createdAt
        }.find { (it.account eq account) and (it.deleted eq false) }
    }

    /**
     * 获取账号密码
     *
     * @param account
     * @return
     */
    override fun getByPassword(account: String): Account? {
        return database.accounts.filterColumns {
            it.columns - it.deleted - it.createdAt
        }.find { (it.account eq account) and (it.deleted eq false) }
    }

    /**
     * 分页返回账号列表
     *
     * @param account
     * @param username
     * @param status
     * @param createdBy
     * @param createdAt
     * @param createdStartAt
     * @param createdEndAt
     * @param updatedBy
     * @param updatedAt
     * @param updatedStartAt
     * @param updatedEndAt
     * @param current
     * @param pageSize
     * @return
     */
    override fun list(
        account: String?,
        username: String?,
        status: IntArray?,
        createdBy: Int?,
        createdAt: String?,
        createdStartAt: LocalDateTime?,
        createdEndAt: LocalDateTime?,
        updatedBy: Int?,
        updatedAt: String?,
        updatedStartAt: LocalDateTime?,
        updatedEndAt: LocalDateTime?,
        current: Int,
        pageSize: Int
    ): PageVO<AccountPageVO> {
        val createdAccount = Accounts.aliased("created")
        val updatedAccount = Accounts.aliased("updated")

        val query = database.from(Accounts)
            .leftJoin(createdAccount, on = Accounts.createdBy eq createdAccount.id)
            .leftJoin(updatedAccount, on = Accounts.updatedBy eq updatedAccount.id)
            .select(Accounts.columns - Accounts.password - Accounts.deleted
                    + createdAccount.username + updatedAccount.username)
            .whereWithConditions {
                if (account != null) {
                    it += Accounts.account like "%${account}%"
                }

                if (username != null) {
                    it += Accounts.username like "%${username}%"
                }

                if (status != null) {
                    it += Accounts.status inList status.toList()
                }

                if (createdBy != null) {
                    it += Accounts.createdBy eq createdBy
                }

                if (createdStartAt != null && createdEndAt != null) {
                    it += Accounts.createdAt between createdStartAt..createdEndAt
                }

                if (createdStartAt != null && createdEndAt == null) {
                    it += Accounts.createdAt greaterEq createdStartAt
                }

                if (createdStartAt == null && createdEndAt != null) {
                    it += Accounts.createdAt lessEq createdEndAt
                }

                if (updatedBy != null) {
                    it += Accounts.updatedBy eq updatedBy
                }

                if (updatedStartAt != null && updatedEndAt != null) {
                    it += Accounts.updatedAt between updatedStartAt..updatedEndAt
                }

                if (updatedStartAt != null && updatedEndAt == null) {
                    it += Accounts.updatedAt greaterEq updatedStartAt
                }

                if (updatedStartAt == null && updatedEndAt != null) {
                    it += Accounts.updatedAt lessEq updatedEndAt
                }
            }
            .orderBy(
                when {
                    createdAt != null && createdAt == "ascend" -> Accounts.createdAt.asc()
                    createdAt != null && createdAt == "descend" -> Accounts.createdAt.desc()
                    updatedAt != null && updatedAt == "ascend" -> Accounts.updatedAt.asc()
                    updatedAt != null && updatedAt == "descend" -> Accounts.updatedAt.desc()
                    else -> Accounts.id.asc()
                }
            )
            .limit(pageSize * (current - 1), pageSize)

        val total = query.totalRecords
        val list = query.map { row ->
            AccountPageVO(
                id = row[Accounts.id],
                account = row[Accounts.account],
                username = row[Accounts.username],
                avatar = row[Accounts.avatar],
                status = row[Accounts.status],
                createdBy = row[Accounts.createdBy],
                createdByName = row[createdAccount.username],
                createdAt = row[Accounts.createdAt],
                updatedBy = row[Accounts.updatedBy],
                updatedByName = row[updatedAccount.username],
                updatedAt = row[Accounts.updatedAt],
            )
        }

        return PageVO(
            total = total,
            totalPage = ceil(total.toDouble() / pageSize).toInt(),
            current = current,
            pageSize = pageSize,
            list = list
        )
    }

    /**
     * 获取所有的账号
     *
     * @return
     */
    override fun list(): List<Account> {
        return database.accounts.filterColumns { listOf(it.id, it.account, it.username) }
            .filter { it.deleted eq false }.toList()
    }

    /**
     * 禁用账号
     *
     * @param id
     * @return
     */
    override fun disabled(id: Int): Boolean {
        return changeStatus(id, 0)
    }

    /**
     * 启用账号
     *
     * @param id
     * @return
     */
    override fun enabled(id: Int): Boolean {
        return changeStatus(id, 1)
    }

    /**
     * 删除账号
     *
     * @param id
     * @return
     */
    override fun deleted(id: Int): Boolean {
        val admin = database.accounts.find { it.id eq id }

        if (admin != null) {
            admin.deleted = true
            return updateAccount(admin)
        }
        return false
    }

    /**
     * 恢复账号
     *
     * @param id
     * @return
     */
    override fun restore(id: Int): Boolean {
        val admin = database.accounts.find { it.id eq id }

        if (admin != null) {
            admin.deleted = false
            return updateAccount(admin)
        }
        return false
    }

    /**
     * 改变账号状态
     *
     * @param id
     * @param status
     * @return
     */
    private fun changeStatus(id: Int, status: Int): Boolean {
        val admin = get(id)

        if (admin != null) {
            admin.status = status
            return updateAccount(admin)
        }
        return false
    }

    /**
     * 更新账号信息
     *
     * @param admin
     * @return
     */
    private fun updateAccount(admin: Account): Boolean {
        val userId = StpUtil.getLoginIdAsInt()
        admin.updatedBy = userId
        admin.updatedAt = LocalDateTime.now()
        val nums = admin.flushChanges()
        return nums > 0
    }

    /**
     * 获取账号的所有角色
     *
     * @param id
     * @return
     */
    override fun getRoles(id: Int): List<Role> {
        return database.from(AccountRoles)
            .leftJoin(Roles, on = AccountRoles.role eq Roles.id)
            .select(Roles.id, Roles.label, Roles.role)
            .where { (AccountRoles.account eq id) and (Roles.deleted eq false) }
            .map { row -> Roles.createEntity(row) }
    }

    /**
     * 更新账号的角色
     *
     * @param id
     * @param roles
     * @return
     */
    override fun updateRoles(id: Int, roles: List<Int>) {
        // 获取账号当前的角色列表 id
        val oldRoles = mutableListOf<Int?>()
        database.from(AccountRoles).select(AccountRoles.role)
            .where { (AccountRoles.account eq id) and (AccountRoles.deleted eq false) }
            .forEach {
                oldRoles.add(it[AccountRoles.role])
            }

        // 如果当前账号没有角色则直接将 roles 赋予给它
        if (oldRoles.isEmpty()) {
            database.batchInsert(AccountRoles) {
                roles.forEach { role ->
                    item {
                        set(it.account, id)
                        set(it.role, role)
                        set(it.createdBy, StpUtil.getLoginIdAsInt())
                    }
                }
            }
        } else { // 当前账号存在的角色
            // 新增的角色 id
            val increaseRoles = roles - oldRoles
            // 新增角色
            if (increaseRoles.isNotEmpty()) {
                increaseRoles.forEach { role ->
                    database.insertOrUpdate(AccountRoles) {
                        set(it.account, id)
                        set(it.role, role)
                        onDuplicateKey {
                            set(it.deleted, false)
                            set(it.updatedAt, LocalDateTime.now())
                            set(it.updatedBy, StpUtil.getLoginIdAsInt())
                        }
                    }
                }
            }

            // 删除的角色 id
            val reduceRoles = oldRoles - roles
            // 删除角色
            if (reduceRoles.isNotEmpty()) {
                database.batchUpdate(AccountRoles) {
                    reduceRoles.forEach { role ->
                        if (role != null) {
                            item {
                                set(it.deleted, true)
                                set(it.updatedAt, LocalDateTime.now())
                                set(it.updatedBy, StpUtil.getLoginIdAsInt())
                                where {
                                    (it.account eq id) and
                                            (it.role eq role)
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 获取账号的所有菜单
     *
     * @param id
     * @return
     */
    override fun getMenus(id: Int): Set<Menu> {
        val roles = getRoles(id)
        val isSuper = roles.any { it.role == "super-admin" }
        val roleIds = roles.map { it.id }
        return if (isSuper) {
            database.menus.filter { it.deleted eq false }.toSet()
        } else {
            database.from(RoleMenus)
                .leftJoin(Menus, on = RoleMenus.menu eq Menus.id)
                .select(Menus.columns)
                .where { (RoleMenus.role inList roleIds) and (RoleMenus.deleted eq false) }
                .map { row -> Menus.createEntity(row) }
                .toSet()
        }
    }

    /**
     * 获取账号的所有权限
     *
     * @param id
     * @return
     */
    override fun getPermissions(id: Int): Set<Permission> {
        TODO("Not yet implemented")
    }
}
